//
//	(c) 2009 Andrew Kalmacky
//

#ifndef _AK_DOM_ABSTRACTS_H_
#define _AK_DOM_ABSTRACTS_H_

#include <list>
#include <map>
#include "../logger/logger.h"
#include "../ltm/ltm.h"
#include "../string/ltm_string.h"

namespace ak
{
	namespace dom
	{
		using ak::ltm::Object;
		using ak::ltm::SharedObject;
		using ak::ltm::pin;
		using ak::ltm::master;
		using ak::ltm::weak;
		using ak::string::master_str;

		struct unsupported_operation_exception
		{
			master_str text;
			unsupported_operation_exception(const temp_str& text)
				: text(text)
			{}
		};

		class TypeName;
		class NodeName;
		class Type;

		class EnumTag;

		class const_data;
		class data;

		class Field;
		class Node;
		class Domain;

		enum TypeId
		{
			TYPE_INT,
			TYPE_ENUM,
			TYPE_FLOAT,
			TYPE_STRING,
			TYPE_VECTOR,
			TYPE_STRUCT,
			TYPE_PTR,
			TYPE_CLASS,
		};

		class MetaObject: public SharedObject
		{
		public:
			virtual string get_printable_name() const =0;
		};

		template<typename T>
		class name_iterator
		{
			typedef typename T::children_t::const_iterator iterator_t;
			pin<T> base;
			iterator_t it;
		public:
			name_iterator(const pin<T>& base, const iterator_t& it)
				: base(base)
				, it(it)
			{}
			pin<T> get_next() const
			{
				return it == b->children.end() ? NULL : *it++;
			}
		};

		class TypeName: public MetaObject
		{
			friend class name_iterator<TypeName>;
			typedef std::map< string, master<TypeName> > children_t;
			string name;
			children_t children;
			master<Type> target;
			weak<TypeName> parent;
			virtual void copy(Object*& dst) const;
		public:
			TypeName(const string& name);

			string get_name() const { return name; }
			string get_printable_name() const;

			pin<Type> get_target() const;
			pin<Type> get_target_as_type() const;
			pin<Type> get_target_as_class() const;
			void set_target(const pin<Type>& target);

			void add_child(const pin<TypeName>& child);
			pin<TypeName> get_child(const string& name) const;
			name_iterator<TypeName> get_children_iterator() const;
			pin<TypeName> get_parent() const;
		};

		class NodeName: public MetaObject
		{
			friend class name_iterator<NodeName>;
			typedef std::map< string, weak<NodeName> > children_t;
			children_t children;
			weak<Node> target;
			master<NodeName> parent;
			string name;
			virtual void copy(Object*& dst) const;
		public:
			NodeName(const pin<NodeName>& domain, const string& name, const pin<Node>& target);

			string get_name() const { return name; }
			string get_printable_name() const;

			pin<Node> get_target() const;

			pin<NodeName> get_child(const string& name) const;
			name_iterator<NodeName> get_children_iterator() const;
			pin<NodeName> get_parent() const;
		};

		class Node: public Object
		{
		public:
			Node(const pin<Type>& type);
			Node(const Node& src);
			~Node();
			pin<Type> get_type() const { return type; }
			pin<NodeName> get_name() { return name; }
			pin<NodeName> set_name(pin<NodeName>& name) { this->name = name; }
			const_data get_data() const;
			data get_data();
		protected:
			master<NodeName> name;
			master<Type> type;
			void* extension;
		};

		class Type: public MetaObject
		{
			friend class const_data;
			friend class data;
		protected:
			//
			// data access (used with data and const data wrappers
			//
			virtual int read_int(void* ptr) const;
			virtual long64 read_long64(void* ptr) const;
			virtual double read_double(void* ptr) const;
			virtual pin<EnumTag> read_tag(void* ptr) const;
			virtual string read_string(void* ptr) const;
			virtual pin<Node> read_ptr(void* ptr) const;
			virtual size_t read_items_count(void* ptr) const;
			virtual void* read_item_offset(void* ptr, size_t index) const;

			virtual void write_int(void* ptr, int val) const;
			virtual void write_long64(void* ptr, long64 val) const;
			virtual void write_double(void* ptr, double val) const;
			virtual void write_tag(void* ptr, const pin<EnumTag>& val) const;
			virtual void write_string(void* ptr, const string& val) const;
			virtual void write_ptr(void* ptr, const pin<Node>& val) const;
			virtual void insert_items(void* ptr, size_t index, size_t count) const;
			virtual void delete_items(void* ptr, size_t index, size_t count) const;

			//
			// version support
			//
			virtual size_t get_instance_fields_count(void* ptr) const;
			virtual void update_instance_version(void* ptr) const;

		public:
			Type(const pin<TypeName>& name)
				: name(name)
			{}
			//
			// allocation for ref types
			//
			virtual pin<Node> make_instance() const;
			//
			// allocation for value types
			//
			virtual size_t get_instance_size() const;
			virtual void construct(void* ptr) const;
			virtual void destruct(void* ptr) const;
			virtual void copy(void* dst_ptr, void* src_ptr) const;
			//
			// type info
			//
			pin<TypeName> get_name() const { return name; }
			virtual string get_printable_name() const;
			virtual TypeId get_type_id() const =0;

			virtual bool is_int_signed() const;

			virtual size_t get_enum_tags_count() const;
			virtual pin<EnumTag> get_enum_tag(size_t index) const;

			virtual size_t get_float_m_width() const;
			virtual size_t get_float_p_width() const;

			virtual size_t get_vector_static_items_count() const;
			virtual pin<Type> get_vector_items_type() const;

			virtual size_t get_struct_fields_count() const;
			virtual pin<Field> get_field(size_t field_index) const;
			virtual pin<Field> get_field(const string& name, const pin<Type> type, bool write_compatible) const;
			virtual bool check_if_contains(const pin<Field>& field) const;

		protected:
			weak<TypeName> name;
		};

		class Field: public MetaObject
		{
			friend class const_data;
			friend class data;
		public:
			Field(const string& name, const pin<Type>& type, size_t ordinal = 0)
				: name(name)
				, type(type)
				, ordinal(ordinal)
			{}
			virtual string get_printable_name() const;
			pin<Type> get_type() const { return type; }
			size_t get_ordinal() { return ordinal; }
		protected:
			string name;
			master<Type> type;
			size_t ordinal;
			virtual void* read_field_offset(void* ptr) const =0;
			virtual void* read_extra_field_offset(void* ptr) const =0;
		};

		class EnumTag: public MetaObject
		{
			string name;
			size_t value;
		public:
			EnumTag(const string& name, size_t value);
			virtual string get_printable_name() const;
			size_t get_val() const { return value; }
		};

		class Domain: public ltm::Object
		{
			virtual pin<Node> get_root() const;
			virtual pin<Node> set_root(pin<Node>& val);
			virtual pin<TypeName> get_class_names();
			virtual pin<TypeName> get_type_names();
			virtual pin<NodeName> get_object_names();

			virtual pin<Type> get_int_type(int width, bool is_signed) const;
			virtual pin<Type> get_float_type(int width) const;
			virtual pin<Type> get_vector_type(const pin<Type>& element_type, int size = 0) const;
			virtual pin<Type> get_str_type() const;
			virtual pin<Type> get_pointer_type(bool owner) const;

			virtual void register_new_type(const pin<Type>& val);
			virtual void register_new_class(const pin<Type>& val);

			virtual pin<Type> extend_struct(const pin<TypeName>& name, const std::list< pin<Field> >& extra_fields);
			virtual pin<Type> extend_enum(const pin<TypeName>& name, const std::list< pin<EnumTag> >& extra_tags);
			virtual pin<Type>& extend_class(const pin<TypeName>& name, const std::list< pin<Field> >& extra_fields);
		protected:
			master<Node> root;
			master<TypeName> classes_root;
			master<TypeName> structs_root;
			master<TypeName> enums_root;
			master<NodeName> object_registry;
			master<Type> t_s8, t_s16, t_s32, t_s64, t_u8, t_u16, t_u32, t_u64;
			master<Type> t_f32, t_f64;
			master<Type> t_string, t_master_ptr, t_weak_ptr;

			struct vector_info
			{
				master<Type> element_type;
				size_t size;

				vector_info(const pin<Type>& element_type, size_t size)
					: element_type(element_type)
					, size(size)
				{}
				friend bool operator< (const vector_info& a, const vector_info& b)
				{
					return (void*)a.element_type == (void*)b.element_type ?
						a.size < b.size :
					(void*)a.element_type < (void*)b.element_type;
				}
				friend bool operator== (const vector_info& a, const vector_info& b)
				{
					return (void*)a.element_type == (void*)b.element_type && a.size == b.size;
				}
			};
			map<vector_info, Type> vector_types;
		};

		class const_data
		{
			friend class Node;
		protected:
			pin<Node> node;
			void* val;
			pin<Type> type;

			const_data(const pin<Node>& node, const void* data, const pin<Type>& type)
				: node(node)
				, val(const_cast<void*>(data))
				, type(type)
			{}
		public:
			pin<Type> get_type() const { return type; }
			const_data open_field(const pin<Field>& field)
			{
				type->check_if_contains(field);
				return const_data(node, field->read_field_offset(val), field->get_type());
			}
			const_data open_vector_item(size_t index)
			{
				return const_data(node, type->read_item_offset(val, index), type->get_vector_items_type());
			}
			int get_int() const
			{
				return type->read_int(val);
			}
			long64 get_long() const
			{
				return type->read_long64(val);
			}
			double get_double() const
			{
				return type->read_double(val);
			}
			string get_string() const
			{
				return type->read_string(val);
			}
			size_t get_items_count() const
			{
				return type->read_items_count(val);
			}
			pin<EnumTag> get_tag() const
			{
				return type->read_tag(val);
			}
			pin<Node> get_ptr() const
			{
				return type->read_ptr(val);
			}
		};

		class data: public const_data
		{
			friend class Node;
			data(const pin<Node>& node, void* data, const pin<Type>& type)
				: const_data(node, data, type)
			{}
		public:
			data open_field(const pin<Field>& field)
			{
				type->check_if_contains(field);
				return data(node, field->read_field_offset(val), field->get_type());
			}
			data open_vector_item(size_t index)
			{
				return data(node, type->read_item_offset(val, index), type->get_vector_items_type());
			}
			void delete_items(size_t at, size_t count) const
			{
				type->delete_items(val, at, count);
			}
			void insert_items(size_t at, size_t count) const
			{
				type->insert_items(val, at, count);
			}
			void set(int val) const
			{
				type->write_int(this->val, val);
			}
			void set(long val) const
			{
				type->write_long64(this->val, val);
			}
			void set(double val) const
			{
				type->write_double(this->val, val);
			}
			void set(const string& val) const
			{
				type->write_string(this->val, val);
			}
			void set(const pin<EnumTag>& val) const
			{
				type->write_tag(this->val, val);
			}
			void set(const pin<Node>& val) const
			{
				type->write_ptr(this->val, val);
			}
		};
	}
}

#endif // _AK_DOM_ABSTRACTS_H_
